/**
 * Copyright (C) 2009-2012, KingHool.com
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses.
 */
package com.kinghool.smodel.rule;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.kinghool.ebos.commons.utils.Assert;
import com.kinghool.ebos.commons.utils.Utils;
import com.kinghool.smodel.IModel;
import com.kinghool.smodel.ITreeModel;

/**
 * 
 * @Author Allen Deng(dengtailin@gmail.com) 
 */
public class ChildCountRule extends BaseModelRule implements IModelRule {
	public final static String ANY = "*"; 
	public final static String SINGLE = "1"; 
	public final static String ZERO_TO_ONE = "0.1"; 
	public final static String ONE_TO_MANY = "1.*"; 
    private Map<String, String> childCountConstrants = new HashMap<String, String>();
    
    public ChildCountRule(int severity, String[] childCountConstrants) {
    	super(severity);
		if (! Assert.isNullOrEmpty(childCountConstrants)) {
			if (childCountConstrants.length % 2 != 0) {
				throw new IllegalArgumentException("The argumenet " + childCountConstrants + " is invalid, should have even count of items");
			}
		}
		this.childCountConstrants = makeConstrantPairBy(childCountConstrants);
	}    
    
	public ChildCountRule(int severity, String childName, String childCountConstrant) {
		this(severity, makeConstrantPairBy(childName, childCountConstrant));		
	}
	 
	public ChildCountRule(int severity, Map<String, String> childCountConstrants) {
		super(severity); 
		this.childCountConstrants = childCountConstrants;
	}
	 
	public List<String> validate(IModel model) {
		List<String> errorMessages = Utils.newList();
		if (model instanceof ITreeModel) {
			ITreeModel treeModel = (ITreeModel) model;
			for (String childName : childCountConstrants.keySet()) {
				List<ITreeModel> childrenModel = treeModel.getTreeChild(childName);
				
				int childCnt = childrenModel.size();
				String constrant = childCountConstrants.get(childName);
				
				if (ANY.equals(constrant)) {
					continue; 
				} else if (ONE_TO_MANY.equals(constrant) && childCnt >= 1) {
					continue;
				} else if (ZERO_TO_ONE.equals(constrant) && childCnt <= 1) {
					continue;
				} else {
					try {
				  	   if (Long.parseLong(constrant) == childCnt) {
							 continue;
					   }
					} catch (Exception e) {
						 // quietly do nothing
					} 
					errorMessages.add("Bad format for the ITreeModel:\n" +
						            "The count(value=" + childCnt + ") of child "+ childName + 
						            " for the ITreeModel ["+ model.toString() + "] is not valid, it requires '" + 
						            constrant + "' " + "children."); 
					 
				}
				
			}  
		}
		return errorMessages;   
	}
	
	private static Map<String, String> makeConstrantPairBy(String childName, String childCountConstrant) {
		Map<String, String> constrantPair = new HashMap<String, String>();
		constrantPair.put(childName, childCountConstrant);
		return constrantPair;
	}
	
	private static Map<String, String> makeConstrantPairBy(String[] childCountConstrants) {
		Map<String, String> constrantPair = Utils.newMap();
		for (int i = 0; i < childCountConstrants.length; i = i + 2) { 
			constrantPair.put(childCountConstrants[i], childCountConstrants[i + 1]);
		}
		return constrantPair;
	} 
	
	public boolean isMandatory(String childName) {
		if (Assert.isNotNullNotEmpty(childName)) { 
			String constrant = childCountConstrants.get(childName);
			return !ANY.equals(constrant) && !ZERO_TO_ONE.equals(constrant);
		}
		return false;
	}
}
